<template>
	<view class="vi_tree">
		<!-- 搜索 -->
		<view class="vi_tree_search" v-if="searchSetting.open">
			<vi-searchbar
				:type="setting.type"
				:color="color"
				:radius="setting.radius"
				:height="searchSetting.height"
				:bgColor="searchSetting.bgColor"
				:plain="searchSetting.plain"
				:placeholder="searchSetting.placeholder"
				:buttonStyle="searchSetting.buttonStyle"
				:value="keyword"
				:text="textSetting"
				@search="doSearch"></vi-searchbar>
		</view>
		<!-- tree模式 -->
		<scroll-view class="vi_tree_nodeTree" :style="{'height':scrollHeight}" scroll-x scroll-y v-if="isNodeTree" @touchmove.stop>
			<view class="vi_tree_empty" v-if="!inList&&!isEmpty">
				{{textSetting.tipNoData}}
			</view>
			<view class="vi_tree_empty" v-if="isEmpty">
				{{textSetting.tipNoResult}}
			</view>
			<view v-for="(flatNode, index) in flatList" :key="index">
				<view class="vi_tree_treeNode" v-if="flatNode._handle._nodeShow">
					<!-- 占位前缀 -->
					<view class="vi_tree_nodePrefix" v-for="idx of flatNode.level" :key="idx"
						:style="{'width':setting.nodeIndent+'em'}"></view>
					<!-- 展开状态图标 -->
					<view class="vi_tree_iconStatus" v-if="setting.useIconStatus"
						:style="{'margin-right':setting.iconSpace+'em'}"
						@click="onNodeClick(flatNode, 'extend')">
						<text :class="flatNode.hasChild?'iconfont '+(flatNode._handle.open?iconSetting.open:iconSetting.close):'vi_tree_holder'"></text>
					</view>
					<!-- 选择框 -->
					<view class="vi_tree_check" v-if="isCheckbox||(isRadio&&(flatNode.type=='leaf'||selectNode))"
						:style="{'margin-right':setting.iconSpace+'em'}"
						@click="onNodeClick(flatNode, 'check')">
						<vi-style-controller
							:type="setting.type"
							:color="color"
							:radius="isCheckbox?'square':'circle'"
							width="1.2em"
							height="1.2em"
							:borderWidth="1"
							:plain="checkboxPlain"
							:disabled="flatNode._handle.disabled||disabled"
							:readOnly="flatNode._handle.readOnly||readOnly||flatNode._handle._locked"
							borderBox
							textHide
							:active="!!flatNode._handle._checked"
							basicBorder
							basicText>
							<text class="iconfont" :class="flatNode.type=='leaf'||flatNode._handle._checked!='parts'?iconSetting.checked :iconSetting.parts"></text>
						</vi-style-controller>
					</view>
					<!-- 节点图标 -->
					<view class="vi_tree_iconNode" v-if="setting.useIconNode"
						:style="{'margin-right':setting.iconSpace+'em'}"
						@click="onNodeClick(flatNode, 'icon')">
						<vi-style-controller
							control="text"
							:type="setting.type"
							:color="color"
							:disabled="flatNode._handle.disabled||disabled"
							:readOnly="flatNode._handle.readOnly||readOnly||flatNode._handle._locked"
							:active="isShow&&flatNode._handle._checked==true">
							<text class="iconfont"
								:class="iconSetting[flatNode.type+'Open']"
								v-if="flatNode._handle.open&&iconSetting[flatNode.type+'Open']"></text>
							<text class="iconfont" :class="iconSetting[flatNode.type]" v-else></text>
						</vi-style-controller>
					</view>
					<!-- 标题 -->
					<view class="vi_tree_nodeContent" @click="onNodeClick(flatNode, 'title')">
						<vi-style-controller
							control="text"
							:type="setting.type"
							:color="color"
							:textColor="flatNode._handle.textColor||''"
							inline
							:disabled="flatNode._handle.disabled||disabled"
							:readOnly="flatNode._handle.readOnly||readOnly||flatNode._handle._locked"
							:active="isShow&&flatNode._handle._checked==true">{{flatNode._name}}</vi-style-controller>
					</view>
				</view>
			</view>
		</scroll-view>
		<!-- page模式 -->
		<view class="vi_tree_pageNav" :style="{'background-color':setting.pageNavBgColor}" v-if="isPageTree">
			<view class="vi_tree_navRoot" :style="{'height':setting.pageNavHeight+'em'}" @click="nextPage(rootPage)">
				<vi-style-controller
					control="text"
					:type="setting.type"
					:color="color"
					inline
					alignLeft
					:active="!!pageNav.length">
					<view class="vi_tree_navText">
						<text>{{rootPage.name}}</text>
						<text class="iconfont" :class="iconSetting.navNext" v-if="pageNav.length"></text>
					</view>
				</vi-style-controller>
			</view>
			<scroll-view class="vi_tree_navItems" scroll-x>
				<view class="vi_tree_navItem" :style="{'height':setting.pageNavHeight+'em'}" v-for="(nav, index) in pageNav" :key="index" @click="nextPage(nav.node)">
					<vi-style-controller
						control="text"
						:type="setting.type"
						:color="color"
						inline
						:active="nav.next">
						<view class="vi_tree_navText">
							<text>{{nav.name}}</text>
							<text class="iconfont" :class="iconSetting.navNext" v-if="nav.next"></text>
						</view>
					</vi-style-controller>
				</view>
			</scroll-view>
		</view>
		<scroll-view class="vi_pageTree" :style="{'height':scrollHeight}" scroll-y v-if="isPageTree&&currPage" @touchmove.stop>
			<view class="vi_tree_empty" v-if="!inList&&!isEmpty">
				{{textSetting.tipNoData}}
			</view>
			<view class="vi_tree_empty" v-if="isEmpty">
				{{textSetting.tipNoResult}}
			</view>
			<view class="vi_tree_pageItem" v-for="(node, index) in currPage[keySetting.children]" :key="index" v-if="!node._hide">
				<!-- 选择框 -->
				<view class="vi_tree_check" v-if="isCheckbox||(isRadio&&(node._nodeType=='leaf'||selectNode))" @click="onNodeClick(node, 'check')">
					<vi-style-controller
						:type="setting.type"
						:color="color"
						:radius="isCheckbox?'square':'circle'"
						width="1.4em"
						height="1.4em"
						:borderWidth="1"
						:plain="checkboxPlain"
						:disabled="node.disabled||disabled"
						:readOnly="node.readOnly||readOnly||node._locked"
						borderBox
						textHide
						:active="!!node._checked"
						basicBorder
						basicText>
						<text class="iconfont" :class="node._nodeType=='leaf'||node._checked!='parts'?iconSetting.checked :iconSetting.parts"></text>
					</vi-style-controller>
				</view>
				<!-- 节点图标 -->
				<view class="vi_tree_iconNode" v-if="setting.useIconNode" @click="onNodeClick(node, 'icon')">
					<vi-style-controller
						control="text"
						:type="setting.type"
						:color="color"
						:disabled="node.disabled||disabled"
						:readOnly="node.readOnly||readOnly||node._locked"
						:active="isShow&&!!node._checked">
						<text class="iconfont"
							:class="iconSetting[node._nodeType+'Open']"
							v-if="node.open&&iconSetting[node._nodeType+'Open']"></text>
						<text class="iconfont" :class="iconSetting[node._nodeType]" v-else></text>
					</vi-style-controller>
				</view>
				<!-- 标题 -->
				<view class="vi_tree_nodeContent" @click="onNodeClick(node, 'title')">
					<vi-style-controller
						control="text"
						:type="setting.type"
						:color="color"
						:textColor="node.textColor||''"
						inline
						:disabled="node.disabled||disabled"
						:readOnly="node.readOnly||readOnly||node._locked"
						:active="isShow&&!!node._checked">{{node[keySetting.name]}}</vi-style-controller>
				</view>
				<!-- 展开状态图标 -->
				<view class="vi_tree_iconStatus" v-if="setting.useIconStatus" @click="nextPage(node)">
					<text :class="node._hasChild?'iconfont '+iconSetting.navNext :'vi_tree_holder'"></text>
				</view>
			</view>
		</scroll-view>
		<!-- 工具栏 -->
		<view class="vi_tree_toolbar" :style="{'margin': '0.5em', 'height': setting.toolbarHeight+'em'}" v-if="setting.useToolbar">
			<view class="vi_tree_selectCount" v-if="isSelect" @click="showSelectList">
				<vi-style-controller
					:type="selectList.length?'bgSuccess':'bgError'"
					radius="circle"
					height="1.5em"
					active>
					<text class="vi_tree_number">{{selectList.length||0}}</text>
				</vi-style-controller>
			</view>
			<view class="vi_tree_submitBtn">
				<vi-button
					:type="setting.type"
					:color="color"
					:radius="setting.radius"
					:size="size"
					:width="setting.submitWidth"
					:height="setting.toolbarHeight+'em'"
					:plain="submitPlain"
					@click="submit">{{textSetting.confirm}}</vi-button>
			</view>
		</view>
		<!-- 选择列表 -->
		<vi-drawer ref="selectList" position="absolute" :top="listTop" :bottom="listBottom" :style="{'height':listHeight}">
			<scroll-view class="vi_tree_selectList" scroll-y @touchmove.stop>
				<view class="vi_tree_selectItem" v-for="(node, index) in selectList" :key="index">
					<!-- 节点图标 -->
					<view class="vi_tree_iconNode" v-if="setting.useIconNode">
						<vi-style-controller
							control="text"
							:type="setting.type"
							:color="color">
							<text class="iconfont"
								:class="iconSetting[node._nodeType+'Open']"
								v-if="node.open&&iconSetting[node._nodeType+'Open']"></text>
							<text class="iconfont" :class="iconSetting[node._nodeType]" v-else></text>
						</vi-style-controller>
					</view>
					<!-- 标题 -->
					<view class="vi_tree_nodeContent">
						<vi-style-controller
							control="text"
							:type="setting.type"
							:color="color"
							inline>{{node[keySetting.name]}}</vi-style-controller>
					</view>
					<!-- 删除按钮 -->
					<view class="vi_tree_delete" @click="delItem(node)">
						<vi-style-controller
							type="bgError"
							radius="circle"
							width="1.4em"
							height="1.4em"
							:plain="checkboxPlain"
							:disabled="node.disabled||disabled"
							:readOnly="node.readOnly||readOnly"
							borderBox
							active>
							<text class="iconfont" :class="iconSetting.delete"></text>
						</vi-style-controller>
					</view>
				</view>
			</scroll-view>
		</vi-drawer>
	</view>
</template>

<script>
	export default {
		name:"",
		props:{
			type:{
				type:[String,null],
				default:null
			},
			color:{
				type:[String,null],
				default:""
			},
			//提交按钮、搜索条用
			radius:{
				type:[String,Number,null],
				default:null
			},
			size:{
				type:[String,null],
				default: ""
			},
			//树样式，tree普通树；page节点列表
			treeStyle:{
				type:[String,null],
				default:"",
			},
			dataList:{
				type:[Array,Object,null],
				default:()=>[
					// {
					// 	name:"标题",
					// 	children:[]
					// }
				]
			},
			//值列表
			value:{
				type:[Array,String,Number,null],
				default:()=>[]
			},
			searchSet:{
				type:[Object,null],
				default:()=>({})
			},
			//只搜索叶子
			searchLeaf:{
				type:[Boolean,null],
				default:null
			},
			keyword:{
				type:[String,null],
				default: ""
			},
			checkboxPlain:{
				type:[Boolean,null],
				default:false
			},
			//子节点缩进，单位字符，tree模式有效
			nodeIndent:{
				type:[Number,null],
				default:null
			},
			//图标与标题间隙，单位字符，tree模式有效
			iconSpace:{
				type:[Number,null],
				default:null
			},
			//使用展开状态图标
			useIconStatus:{
				type:[Boolean,null],
				default:null
			},
			//使用节点图标
			useIconNode:{
				type:[Boolean,null],
				default:null
			},
			//show显示模式，radio单选模式，checkbox多选模式，edit编辑模式（暂无）
			mode:{
				type:[String,null],
				default:""
			},
			//根、节点可点击选中，show、radio模式有效
			selectNode:{
				type:[Boolean,null],
				default:false
			},
			//自动计算节点类型，开启则将第一层级节点设为根，无children的节点设为叶子。关闭则须在节点手动设置_isRoot=true指定根，_isLeaf=true指定叶子
			autoNodeType:{
				type:[Boolean,null],
				default:null
			},
			//返回结果格式，可选：
			//node，返回节点
			//value、空，返回值
			//其他值没有意义，不向外抛出value将不能变更选中显示
			resFormat:{
				type:[String,null],
				default:""
			},
			pageNavHeight:{
				type:[Number,null],
				default:0
			},
			pageNavBgColor:{
				type:[String,null],
				default:""
			},
			useToolbar:{
				type:[Boolean,null],
				default:null
			},
			submitWidth:{
				type:[String,Number,null],
				default:0
			},
			submitPlain:{
				type:[Boolean,null],
				default:false
			},
			toolbarHeight:{
				type:[Number,null],
				default:0
			},
			disabled:{
				type:[Boolean,null],
				default:false
			},
			readOnly:{
				type:[Boolean,null],
				default:false
			},
			//数据关键字
			keys:{
				type:[Object,null],
				default:()=>({})
			},
			//图标替换
			icon:{
				type:[Object,null],
				default:()=>({})
			},
			text:{
				type:[Object,null],
				default:()=>({})
			},
		},
		data() {
			return {
				inList:null,		//内部数据
				flatList:null,		//扁平化数据
				selectList:[],		//选择列表
				isEmpty: false,		//无数据或搜索无结果
				squareRate:0.2,
				rootPage:{
					isAll: true,
					name: "全部",
					next: false
				},
				currPage:null,
				pageNav:[
					// {
					// 	name:"",
					// 	node:"",
					// 	next: false
					// }
				],
				dicSearchSet:{
					open:false,
					height:2,
					bgColor:"#eee",
					plain:false,
					// placeholder:"", 使用默认
					//搜索按钮样式，可选：
					//iconLeft内部左侧图标，iconRight内部右侧图标
					//outsideLeft外部左侧按钮，outsideRight外部右侧按钮
					//insideLeft内部左侧衔接按钮，insideRight内部右侧衔接按钮
					//active动态搜索(注意性能问题)
					buttonStyle:"iconRight",
				},
				dicKeys:{
					name: "name",//节点标题
					value: "value",//节点值
					children: "children",//节点子列表
				},
				dicIcon:{
					root:"icon-tree-root",
					rootOpen:"",
					node:"icon-tree-node",
					nodeOpen:"",
					leaf:"icon-tree-leaf",
					open:"icon-triangle-down",
					close:"icon-triangle-right",
					checked:"icon-checked",
					parts:"icon-tree-parts",
					navNext:"icon-arr-right",
					delete:"icon-delete"
				},
				dicText:{
					all: "全部",
					confirm: "确定",
					tipNoData: "暂无数据",
					tipNoResult: "无搜索结果",
					tipTreeEmptyError: "请先选择选项",
				},
			}
		},
		computed: {
			//将默认值内含到css、计算属性等地方，只内含真值
			//规避特殊使用的情况下，由prop传入undefined、null等无效数据导致的，默认值失效问题。例如组件嵌套且需要组件间参数传递
			//HBuilder X3.3.13; 微信开发者工具stable 1.05.2111300
			//微信小程序会对传入的undefined、null值根据参数类型输出不同空值(false、""、null、0)，参数类型为null或未指定参数类型则输出null
			//H5会对传入的undefined取默认值，未设默认值则输出undefined。对传入的null值固定输出null
			//做参数传递时需警惕两者行为不同而造成的陷阱，主要问题出现在微信小程序的空值转化，输出非用户传入的空值、假值，使默认真值丢失
			//兼容空值取默认值的可行方案目前为，参数类型加入null如[Boolean,null]，判断传入参数是否为定义的类型，是则使用，否则赋予默认值
			setting(){
				return {
					type: this.$api.isDefined(this.type)? this.type :"primary",
					radius: this.$api.isDefined(this.radius)? this.radius :"circle",
					nodeIndent: this.$api.isValue(this.nodeIndent)? this.nodeIndent :1.5,
					iconSpace: this.$api.isValue(this.iconSpace)? this.iconSpace :0.25,
					useIconStatus: this.$api.isBoolean(this.useIconStatus)? this.useIconStatus :true,
					useIconNode: this.$api.isBoolean(this.useIconNode)? this.useIconNode :true,
					pageNavHeight: this.pageNavHeight ||2.5,
					useToolbar: this.$api.isBoolean(this.useToolbar)? this.useToolbar :true,
					toolbarHeight: this.toolbarHeight ||2.5,
					treeStyle: this.treeStyle ||"tree",
					searchLeaf: this.$api.isBoolean(this.searchLeaf)? this.searchLeaf :true,
					autoNodeType: this.$api.isBoolean(this.autoNodeType)? this.autoNodeType :true,
					mode: this.mode ||"show",
					pageNavBgColor: this.pageNavBgColor || "#eee",
					submitWidth: this.$api.isDefined(this.submitWidth)? this.submitWidth :"100%",
				}
			},
			isNodeTree(){
				return this.setting.treeStyle=="tree"
			},
			isPageTree(){
				return this.setting.treeStyle=="page"
			},
			isShow(){
				return this.setting.mode=="show"
			},
			isSingle(){
				return this.setting.mode=="show"||this.setting.mode=="radio"
			},
			isSelect(){
				return this.setting.mode=="radio"||this.setting.mode=="checkbox"
			},
			isRadio(){
				return this.setting.mode=="radio"
			},
			isCheckbox(){
				return this.setting.mode=="checkbox"
			},
			//编辑模式与选择、展示模式互斥
			isEdit(){
				return this.setting.mode=="edit"
			},
			searchSetting(){
				if(this.$api.isObject(this.searchSet)){
					return Object.assign({},this.dicSearchSet,this.searchSet)
				}
				return this.dicSearchSet
			},
			keySetting(){
				if(this.$api.isObject(this.keys)){
					return Object.assign({},this.dicKeys,this.keys)
				}
				return this.dicKeys
			},
			iconSetting(){
				if(this.$api.isObject(this.icon)){
					return Object.assign({},this.dicIcon,this.icon)
				}
				return this.dicIcon
			},
			
			//计算各项高度值，scroll-view必须使用高度
			scrollHeight(){
				let count = 0;
				if(this.searchSetting.open){
					count += this.searchSetting.height +1;
				}
				if(this.setting.useToolbar){
					count += this.setting.toolbarHeight +1;
				}
				if(this.isPageTree){
					count += this.setting.pageNavHeight
				}
				return `calc(100% - ${count}em)`
			},
			//选择列表框高度起点
			listTop(){
				if(this.searchSetting.open){
					return this.searchSetting.height +1 +"em";
				}
				return 0
			},
			listBottom(){
				if(this.setting.useToolbar){
					return this.setting.toolbarHeight +1 +"em";
				}
				return 0
			},
			listHeight(){
				return `calc(100% - ${this.listTop} - ${this.listBottom})`
			},
			textSetting(){
				let res;
				if(this.$api.isObject(this.text)){
					res = Object.assign({},this.dicText,this.text)
				}
				res = this.dicText;
				//覆写标题
				this.rootPage.name = res.all;
				return res
			}
		},
		methods: {
			//搜索，只设置节点hide属性
			doSearch(keyword){
				let nameKey = this.keySetting.name;
				//全隐藏
				this.mapChildren(this.inList, (node)=>{
					node["_hide"] = true
				});
				this.isEmpty = true;
				this.mapChildren(this.inList, (node)=>{
					if(!this.$api.isValue(keyword)){
						delete node._hide;
						this.isEmpty = false;
						return
					}
					//只搜索叶子
					if(this.setting.searchLeaf){
						if(node._nodeType=="leaf"&&node[nameKey].includes(keyword)){
							delete node._hide;
							this.isEmpty = false;
							//取消支路隐藏，展开父级
							this.mapParent(node, (item)=>{
								delete item._hide;
								item["open"] = true;
							})
						}
					}else{
						if(node[nameKey].includes(keyword)){
							this.isEmpty = false;
							//取消支路隐藏
							this.mapParent(node, (item)=>{
								delete item._hide;
								item["open"] = true;
							});
							//取消子节点隐藏
							this.mapChildren(node, (item)=>{
								delete item._hide;
							})
						}
					}
				});
				//重设扁平化数据
				if(this.isNodeTree){
					this.flatData()
				}
			},
			//扁平化数据
			flatData(){
				this.flatList =[];
				this.mapChildren(this.inList, (node)=>{
					if(!node._hide){//非隐藏，用于搜索
						this.flatList.push({
							level: node._nodeLevel,
							type: node._nodeType,
							hasChild: node._hasChild,
							_handle: node,
							_name: node[this.keySetting.name]
						})
					}
				});
				this.setNodeShow();
			},
			//节点点击事件
			onNodeClick(item, area){
				let node, res;
				if(this.isNodeTree){
					node = item._handle;
				}else{
					node = item;
				}
				//选中动作，点击区域为选择框或图标或标题
				//分支1：节点类型为叶子
				//分支2：单选模式且开启树枝选择
				//分支3：多选模式
				if(["check","icon","title"].includes(area)&&(
						node._nodeType=="leaf"||
						(this.isSingle&&this.selectNode)||
						this.isCheckbox
					)){
					if(node.disabled||this.disabled||node.readOnly||this.readOnly){
						return
					}
					//显示模式，点击叶子立即提交
					//单选模式，点击叶子选中
					if(this.isSingle){
						//检查选中结果中是否存在不可变更项，如果有则退出
						if(this.selectList.length){
							if(this.$api.arrFind(this.selectList, {func:(item)=>{ return item.disabled||item.readOnly}}) ){
								node["_locked"] = true;
								return
							}
							delete node._locked
						}
						if(this.selectList.length!=1||this.selectList[0]._nodeId!=node._nodeId){
							for(let item of this.selectList){
								delete item._checked;
								this.computeParentChecked(item);
							}
							node["_checked"] = true;
							this.selectList = [node];
							//翻页树，重新检查当前父节点选中状态
							if(this.isPageTree){
								this.computeParentChecked(node);
							}
							res = this.getData()[0];
							this.$emit("change", res);
							if(this.isShow){
								this.$emit("submit", res);
							}
						}
					}else if(this.isCheckbox){
						//多选模式，点击叶子选中/取消；点击树枝全选/全取消子节点
						if(node._nodeType=="leaf"){
							node["_checked"] = !node._checked;
							if(node._checked){//选中
								if(!this.$api.arrFind(this.selectList, {key:"_nodeId", value:node._nodeId})){
									this.selectList.push(node);
								}
							}else{//取消
								this.$api.arrDel(this.selectList, {key:"_nodeId", value:node._nodeId})
							}
						}else{
							//向下选择，检查如果内部没有叶子则跳出
							let checkLeaf = false;
							this.mapChildren(node, (item)=>{
								if(item._nodeType=="leaf"){
									checkLeaf = true;
									return "return"
								}
							})
							if(checkLeaf){
								delete node._locked;
								this.selectChildren(node, !node._checked?true:false);
							}else{
								node["_locked"] = true
							}
						}
						//重新检查父节点选中状态
						this.computeParentChecked(node);
						res = this.getData();
						this.$emit("change", res);
					}
				}else if(area=="extend"||(["icon","title"].includes(area)&&node._nodeType!="leaf")){
					//展开/收起动作，点击区域为展开图标
					//点击区域为图标或标题，且节点类型非叶子
					if(this.isNodeTree){
						//普通树，展开
						node["open"] = !node["open"];
						this.setNodeShow();
					}else if(this.isPageTree&&this.isSingle){
						//翻页树+单选模式，点击了树枝，进入下一页
						this.nextPage(node)
					}
					//如果内部无任何子节点，给不可点击提示
					let checkNode = false;
					this.mapChildren(node, (item)=>{
						if(node._nodeId != item._nodeId){
							checkNode = true;
							return "return"
						}
					})
					if(checkNode){
						delete node._locked;
					}else{
						node["_locked"] = true
					}
				}
				this.$forceUpdate();
			},
			//page模式进入下一页
			nextPage(node){
				let nameKey = this.keySetting.name,
					childrenKey = this.keySetting.children;
				if(this.currPage==node||
					!node[childrenKey]||
					!node[childrenKey].length){
					return
				}
				this.currPage = node;
				//重建导航
				if(!node.isAll){
					this.pageNav = [{name:node[nameKey], node:node}];
					this.mapParent(node, (item)=>{
						this.pageNav.unshift({
							name:item[nameKey],
							node:item,
							next:true
						})
					})
				}else{
					this.pageNav = []
				}
			},
			//根据父级打开与显示状态判断子节点是否显示
			setNodeShow(){
				this.mapChildren(this.inList, (node, parent)=>{
					node["_nodeShow"] = !parent||(parent.open&&parent._nodeShow)||false;
				})
			},
			//向上计算树枝选中状态，多选时有效
			computeParentChecked(node){
				let childrenKey = this.keySetting.children;
				this.mapParent(node, (node)=>{
					let type = this.$api.typeAsk(node[childrenKey]);
					if(type=="Array"){
						let hasChild = false,
							selectAll = true;
						for(let item of node[childrenKey]){
							if(item._checked){
								hasChild = true
							}
							if(!item._checked||item._checked=="parts"){
								selectAll = false
							}
							if(hasChild&&!selectAll){
								break
							}
						}
						if(selectAll){
							node["_checked"] = true
						}else if(hasChild){
							node["_checked"] = "parts"
						}else{
							node["_checked"] = false
						}
					}else if(type=="Object"){
						node["_checked"] = node[childrenKey]._checked
					}
				})
			},
			//选取当前节点和子节点
			selectChildren(node, state=true){
				this.mapChildren(node, (node)=>{
					if(node.disabled||node.readOnly){
						return
					}
					node["_checked"] = state;
					if(node._nodeType!="leaf"){
						return
					}
					if(state){
						if(!this.$api.arrFind(this.selectList, {key:"_nodeId", value:node._nodeId})){
							this.selectList.push(node);
						}
					}else{
						this.$api.arrDel(this.selectList, {key:"_nodeId", value:node._nodeId})
					}
				})
			},
			//遍历子节点，包括当前节点
			mapChildren(node, func){
				this.$api.treeMap(node, (dataSet)=>{
					let {node, parent, first, last} = dataSet;
					return func(node, parent, first, last);
				}, {key:this.keySetting.children})
			},
			//根据id获取父节点
			getParent(node){
				if(!node){
					return null
				}
				let path = node._idPath;
				if(path.length<=1){
					return null
				}
				let arr = this.inList;
				let parent;
				if(this.$api.isObject(this.inList)){
					arr = [this.inList];
				}
				for(let i=0, len=path.length-1; i<len; i++){
					let id = path[i],
						idx = this.$api.arrFind(arr, {key:"_nodeId", value:id, format:"index"});
					if(idx>-1){
						parent = arr[idx];
						arr = parent[this.keySetting.children];
					}else{
						console.log("树数据错误，查找父级失败");
						return null
					}
				}
				return parent
			},
			//递归遍历父节点，包括当前节点
			mapParentInclude(node, func){
				func(node);
				let parent = this.getParent(node);
				if(parent){
					this.mapParentInclude(parent, func)
				}
			},
			//递归遍历父节点，不包括当前节点
			mapParent(node, func){
				let parent = this.getParent(node);
				if(parent){
					func(parent);
					this.mapParent(parent, func)
				}
			},
			//从数据中取出选中
			takeSelected(){
				this.selectList =[];
				this.mapChildren(this.inList, (node)=>{
					if(node._nodeType=="leaf"&&node._checked){
						this.selectList.push(node)
					}
				})
			},
			//显示选择列表
			showSelectList(){
				this.$refs.selectList.open()
			},
			//在选择列表删除选中
			delItem(node){
				node["_checked"] = false;
				this.$api.arrDel(this.selectList, {key:"_nodeId", value:node._nodeId});
				//重新计算父节点选中状态
				this.computeParentChecked(node);
			},
			submit(){
				if(this.disabled||this.readOnly){
					return
				}
				let res = this.getData();
				if(this.isSingle){
					if(!this.selectList.length){
						this.$api.msg(this.textSetting.tipTreeEmptyError, "error");
						return
					}
					this.$emit("submit", res[0])
				}else{
					if(!this.selectList.length){
						this.$api.msg(this.textSetting.tipTreeEmptyError, "error");
						return
					}
					this.$emit("submit", res)
				}
			},
			//全选，外部使用，多选用
			selectAll(){
				if(this.isCheckbox){
					this.selectChildren(this.inList);
					let res = this.getData();
					this.$emit("change", res);
				}
			},
			//取消选中，外部使用
			clearAll(){
				let res;
				if(this.isSingle){
					for(let i=this.selectList.length-1; i>=0; i--){
						let node = this.selectList[i];
						delete node._locked;
						if(!node.disabled&&!node.readOnly){
							delete node._checked;
							this.selectList.splice(i,1)
						}
					}
					res = this.getData()[0];
					this.$emit("change", res||null)
				}else if(this.isCheckbox){
					this.selectChildren(this.inList, false);
					res = this.getData();
					this.$emit("change", res)
				}
			},
			//返回数据，可外部使用（用options指示结果格式），除count外，必定返回数组
			//外部调用可选format：node节点、value值、count计数、path路径集、key指定键值集
			getData(options){
				let {type="checked", format=this.resFormat, key} = options ||{};
				let res,
					nameKey = this.keySetting.name,
					valueKey = this.keySetting.value;
				if(!format){
					format = "value"
				}
				//指定结果集
				switch(type){
					case "checked":
						res = this.selectList;
						break;
					case "root":
					case "node":
					case "leaf":
						res = [];
						this.mapChildren(this.inList, (node)=>{
							if(node["_nodeType"]==type){
								res.push(node)
							}
						});
						break;
				}
				//指定结果格式
				switch(format){
					case "value":
						return res.map((item)=>{ return item[valueKey]});
					case "count":
						return res.length
					case "key":
						if(key&&typeof(key)=="string"){
							return res.map((item)=>{ return item[key]})
						}
						console.error("key未定义")
						return res
					case "path":
						let newRes =[];
						res.map((node)=>{
							let pathList = [];
							this.mapParent(node, (item)=>{
								let obj = {
									name: item[nameKey]
								}
								if(key){
									obj[key] = item[key]
								}
								pathList.unshift(obj)
							});
							let obj = {
								name: node[nameKey],
								path: pathList,
								node: node
							}
							if(key){
								obj[key] = node[key]
							}
							newRes.push(obj)
						});
						return newRes
					default:
						return res
				}
			},
			//初始化数据
			initData(needEvent){
				if(!this.dataList){
					return
				}
				this.inList = this.$api.clone(this.dataList);
				//如果只有一个根节点，且未定义展开状态，默认展开
				if(this.$api.isObject(this.inList)&&this.inList.open===undefined){
					this.inList["open"] = true
				}else if(this.$api.isArray(this.inList)&&this.inList.length==1&&this.inList.open===undefined){
					this.inList[0]["open"] = true
				}
				//写入公用节点数据；取出选中；对末尾叶子执行父级选中检查
				let id = 0,
					nameKey = this.keySetting.name,
					valueKey = this.keySetting.value,
					childrenKey = this.keySetting.children,
					thisVal = this.value,
					valArr = thisVal||thisVal===0? (this.$api.isArray(thisVal)? thisVal :[thisVal]) :[];
				this.selectList =[];//注意重置选中列表
				this.$api.treeMap(this.inList, (dataSet)=>{
					let {node, level, parent, last, type} = dataSet;
					let nType,
						idPath;
					if(this.setting.autoNodeType){
						nType = type
					}else{
						if(node._isLeaf){
							nType = "leaf"
						}else if(node._isRoot){
							nType = "root"
						}else{
							nType = "node"
						}
					}
					if(parent){
						idPath = [...parent._idPath, id]
					}else{
						idPath = [id]
					}
					node["_nodeId"] =id;
					node["_idPath"] =idPath;
					node["_nodeLevel"] =level;
					node["_nodeType"] =nType;
					node["_nodeShow"] =!parent||parent.open||false;
					let typeAsk = this.$api.typeAsk(node[childrenKey]);
					node["_hasChild"] =typeAsk=="Object"||(typeAsk=="Array"&&node[childrenKey].length);
					id++;
					//设置选中，如果存在value，则使用value设置选中。否则使用数据集中的checked设置
					//多选不允许只选择树枝节点
					if(node._nodeType=="leaf"||(!this.isCheckbox&&this.selectNode)){
						let checked;
						if(valArr.length){
							checked = valArr.indexOf(node[this.keySetting.value]) != -1;
						}else{
							checked = node.checked;
							delete node.checked;//获取后清除
						}
						if(checked){
							node["_checked"] =true;
							this.selectList.push(node);
						}
					}
					//计算父级选中状态
					if(this.isCheckbox&&node._nodeType=="leaf"&&last){
						this.computeParentChecked(node)
					}
				}, {key:childrenKey});
				//模式初始化
				if(this.isNodeTree){
					this.flatData()
				}else if(this.isPageTree){
					if(this.$api.isArray(this.inList)){
						this.rootPage[childrenKey] = this.inList;
						this.currPage = this.rootPage;
					}else if(this.$api.isObject(this.inList)){
						this.rootPage[childrenKey] = [this.inList];
						this.rootPage["next"] = true;
						this.currPage = this.inList;
						this.pageNav = [{
							name:this.inList[nameKey],
							node:this.inList
						}];
					}
				}
				if(needEvent){
					let res = this.getData();
					this.$emit("change", res)
				}
			}
		},
		watch:{
			//初始选中值数组，初始化时由dataList的initData处理，值变化时在这里处理
			"value":{
				handler(newVal){
					let valArr = newVal||newVal===0? (this.$api.isArray(newVal)? newVal : [newVal]) :[];
					//初始化后发生的值变更则处理选中列表
					this.selectList=[];
					this.mapChildren(this.inList,(node, parent, first, last)=>{
						delete node._checked
						//设置选中
						//多选不允许只选择树枝节点
						if(node._nodeType=="leaf"||(!this.isCheckbox&&this.selectNode)){
							//使用值数组设置选中
							if(valArr.includes(node[this.keySetting.value])){
								this.selectList.push(node);
								node["_checked"] =true;
							}
						}
						//多选计算父级选中状态
						if(this.isCheckbox&&node._nodeType=="leaf"&&last){
							this.computeParentChecked(node)
						}
					})
				}
			},
			"dataList":{
				immediate:true,
				handler(newVal){
					this.initData()
				}
			},
		},
		mounted() {
			let res = this.getData();
			this.$emit("ready", this.isRadio? res[0] :res)
		}
	}
</script>

<style lang="scss">
.vi_tree{
	width: 100%;
	height: 100%;
	position: relative;
	.vi_tree_search{
		padding: 0.5em;
	}
	.vi_tree_empty{
		display: flex;
		justify-content: center;
		margin-top: 1em;
		color: $vi-txtColor-tips;
	}
	
	.vi_tree_nodeTree{
		width: 100%;
		.vi_tree_treeNode{
			display: flex;
			align-items: stretch;
			.vi_tree_nodePrefix{
				width: 1em;
				padding: 0.25em 0;
				flex-shrink: 0;
			}
			.vi_tree_nodeContent{
				padding: 0.25em 0;
				min-width: 8em;
			}
			.vi_tree_iconNode{
				display: flex;
				align-items: center;
				flex-shrink: 0;
			}
			.vi_tree_iconStatus{
				display: flex;
				align-items: center;
				flex-shrink: 0;
				color: $vi-txtColor-tips;
				.vi_tree_holder{
					width: 1em;
				}
			}
			.vi_tree_check{
				display: flex;
				align-items: center;
			}
		}
	}
	
	.vi_tree_pageNav{
		flex-shrink: 0;
		display: flex;
		.iconfont{
			margin-left: 0.5em;
		}
		.vi_tree_navRoot{
			flex-shrink: 0;
			width: 4em;
			padding-left: 0.75em;
			display: flex;
			align-items: center;
		}
		.vi_tree_navItems{
			width: calc(100% - 5.25em);
			height: 100%;
			white-space: nowrap;
		}
		.vi_tree_navItem{
			display: inline-flex;
			align-items: center;
			margin-right: 0.5em;
		}
		.vi_tree_navText{
			display: flex;
			align-items: center;
		}
	}
	.vi_tree_pageItem,
	.vi_tree_selectItem{
		height: 3em;
		margin: 0 0.5em;
		display: flex;
		align-items: center;
		border-bottom: 1px solid $vi-borColor-light;
		.vi_tree_check{
			margin-right: 0.5em;
		}
		.vi_tree_iconNode{
			margin-right: 0.5em;
			font-size: 120%;
		}
		.vi_tree_iconStatus{
			flex-grow: 1;
			flex-shrink: 0;
			padding: 0.25em 0;
			margin-right: 0.25em;
			display: flex;
			justify-content: flex-end;
			font-size: 120%;
			color: $vi-txtColor-tips;
		}
	}
	.vi_tree_toolbar{
		display: flex;
		align-items: center;
		.vi_tree_selectCount{
			flex-shrink: 0;
			margin-right: 1em;
			.vi_tree_number{
				padding: 0 0.5em;
			}
		}
		.vi_tree_submitBtn{
			flex-grow: 1;
		}
	}
	
	.vi_tree_selectList{
		width: 100%;
		height: 100%;
		.vi_tree_nodeContent{
			flex-grow: 1;
		}
		.vi_tree_delete{
			padding: 0 0.5em;
			.iconfont::before{
				font-size: 80%;
			}
		}
	}
}
</style>
